﻿using System;
using System.Collections.Generic;
using NetTopologySuite.Index;
using NetTopologySuite.Index.Chain;
using NetTopologySuite.Index.Strtree;

namespace NetTopologySuite.Noding
{
	public class MCIndexSegmentSetMutualIntersector : ISegmentSetMutualIntersector
	{
		public MCIndexSegmentSetMutualIntersector(IEnumerable<ISegmentString> baseSegStrings)
		{
			this.InitBaseSegments(baseSegStrings);
		}

		public ISpatialIndex<MonotoneChain> Index
		{
			get
			{
				return this._index;
			}
		}

		private void InitBaseSegments(IEnumerable<ISegmentString> segStrings)
		{
			foreach (ISegmentString segStr in segStrings)
			{
				this.AddToIndex(segStr);
			}
			this._index.Build();
		}

		private void AddToIndex(ISegmentString segStr)
		{
			foreach (MonotoneChain monotoneChain in MonotoneChainBuilder.GetChains(segStr.Coordinates, segStr))
			{
				this._index.Insert(monotoneChain.Envelope, monotoneChain);
			}
		}

		public void Process(ICollection<ISegmentString> segmentStrings, ISegmentIntersector segmentIntersector)
		{
			List<MonotoneChain> list = new List<MonotoneChain>();
			foreach (ISegmentString segStr in segmentStrings)
			{
				MCIndexSegmentSetMutualIntersector.AddToMonoChains(segStr, list);
			}
			this.IntersectChains(list, segmentIntersector);
		}

		private static void AddToMonoChains(ISegmentString segStr, List<MonotoneChain> monotoneChains)
		{
			foreach (MonotoneChain item in MonotoneChainBuilder.GetChains(segStr.Coordinates, segStr))
			{
				monotoneChains.Add(item);
			}
		}

		private void IntersectChains(IEnumerable<MonotoneChain> monoChains, ISegmentIntersector segmentIntersector)
		{
			MonotoneChainOverlapAction mco = new MCIndexSegmentSetMutualIntersector.SegmentOverlapAction(segmentIntersector);
			foreach (MonotoneChain monotoneChain in monoChains)
			{
				foreach (MonotoneChain mc in this._index.Query(monotoneChain.Envelope))
				{
					monotoneChain.ComputeOverlaps(mc, mco);
					if (segmentIntersector.IsDone)
					{
						return;
					}
				}
			}
		}

		private readonly STRtree<MonotoneChain> _index = new STRtree<MonotoneChain>();

		public class SegmentOverlapAction : MonotoneChainOverlapAction
		{
			public SegmentOverlapAction(ISegmentIntersector si)
			{
				this._si = si;
			}

			public override void Overlap(MonotoneChain mc1, int start1, MonotoneChain mc2, int start2)
			{
				ISegmentString e = (ISegmentString)mc1.Context;
				ISegmentString e2 = (ISegmentString)mc2.Context;
				this._si.ProcessIntersections(e, start1, e2, start2);
			}

			private readonly ISegmentIntersector _si;
		}
	}
}
